package com.htfyun.roobofactory.factorytest;

import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.htfyun.roobofactory.BaseActivity;
import com.htfyun.roobofactory.R;
import com.htfyun.roobofactory.config.RooboBuildConfig;
import com.htfyun.roobofactory.htfyunUtils.NvDataTool;
import com.htfyun.roobofactory.utils.FileReadWrite;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by sst on 2018/7/8.
 */

public class ProximitySensorTestActivity extends BaseActivity implements SensorEventListener {

    private Button btnPass;
    private Button btnFail;

    private Button btnCalibrationNoise;
    private Button btnCalibration20cm;

    private TextView txtInfo;

    private SensorManager mSensorManager;
    private Sensor pSensor;

    // 20170728,最大允许的底噪值，宽度大一点。主要还是卡差值。
    private static final int STK_MAX_NOISE = 2000;
    private static final int STK_MIN_NOISE = 0;

    // 20里面有物体和没有物体的差值的最小值。正常情况下，测试到最小值是 23.
    // 有些有问题的，差值是 15.最大的有54.
    private static final int STK_MIN_20CM_DIFF = 20;
    private static final int STK_MAX_20CM_DIFF = 500;

    private static final int CHECK_DISTANCE = 30;


    private static final String PSENSOR_PATH = "/sys/class/htfyun_ps/config";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_factorytest_proximity_sensor);

        btnPass = (Button) findViewById(R.id.btnPass);
        btnFail = (Button) findViewById(R.id.btnFail);
        btnPass.setOnClickListener(onPassOrFailClickListener);
        btnFail.setOnClickListener(onPassOrFailClickListener);

        btnPass.setVisibility(RooboBuildConfig.FT_ADD_PASS_BTN ? View.VISIBLE : View.GONE);

        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        pSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);

        btnCalibrationNoise = (Button) findViewById(R.id.btnCalibrationNoise);
        btnCalibration20cm = (Button) findViewById(R.id.btnCalibration20cm);

        btnCalibrationNoise.setOnClickListener(onClickListener);
        btnCalibration20cm.setOnClickListener(onClickListener);

        txtInfo = (TextView) findViewById(R.id.txtInfo);

        myHandler = new MyHandler(this);
    }

    @Override
    protected void onResume() {
        super.onResume();

        mSensorManager.registerListener(this, pSensor, SensorManager.SENSOR_DELAY_NORMAL);

        StringBuilder sb = new StringBuilder("");
        int color = Color.RED;
        int currentNoise = getCurrentNoiseValue();
        int currentDifference = getCurrentDifferenceValue();
        if (currentNoise >= STK_MIN_NOISE && currentNoise <= STK_MAX_NOISE
                && currentDifference >= STK_MIN_20CM_DIFF && currentDifference <= STK_MAX_20CM_DIFF) {

            //tune is ok
            String ok = getString(R.string.proximity_sensor_tune_ok, currentNoise, currentDifference);
            sb.append(ok);
            color = Color.GREEN;

        } else {

            String fail = getString(R.string.proximity_sensor_tune_prefix_error,
                    currentNoise, STK_MAX_NOISE,
                    currentDifference, STK_MIN_20CM_DIFF, STK_MAX_20CM_DIFF);
            String more = getString(R.string.proximity_sensor_tune_postfix_fail);
            sb.append(fail);
            sb.append(more);

        }

        txtInfo.setText(sb.toString());
        txtInfo.setTextColor(color);

        btnCalibration20cm.setEnabled(false);

    }

    @Override
    protected void onPause() {
        super.onPause();

        mSensorManager.unregisterListener(this);
    }

    private View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (v == btnCalibrationNoise) {
                pSensorState = PSensorState.tune_noise_start;
                startGetSensorValue(2 * 1000);

            } else if (v == btnCalibration20cm) {

                pSensorState = PSensorState.tune_20cm_start;
                startGetSensorValue(2 * 1000);

            }
        }
    };

    private View.OnClickListener onPassOrFailClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (v == btnPass) {

                setResult(RESULT_OK);
                ActivityCompat.finishAfterTransition(getActivity());

            } else if (v == btnFail) {

                setResult(RESULT_CANCELED);
                ActivityCompat.finishAfterTransition(getActivity());

            }
        }
    };

    private void startTuneOfProximitySensor() {
        String startCmd = "ps_start_tune 1";
        try {
            FileReadWrite.writeStringToFile(PSENSOR_PATH, startCmd);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void writeTuneResultIntoKernel() {
        String noiseCmd = "ps_noise " + sensorValueNoiseAverage;
        int difference = sensorValue20cmAverage - sensorValueNoiseAverage;
        String DiffCmd = "ps_difference " + difference;

        try {
            FileReadWrite.writeStringToFile(PSENSOR_PATH, noiseCmd);
            FileReadWrite.writeStringToFile(PSENSOR_PATH, DiffCmd);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void saveIntoNVRom() {

        int noise = sensorValueNoiseAverage;
        int difference = sensorValue20cmAverage - sensorValueNoiseAverage;

        String log = getString(R.string.proximity_sensor_log_en, noise, difference);

        FactoryTestItemsEnum.test_proximity_sensor.setLog(getActivity(), log);

        NvDataTool.getInstance().setProximitySensorNoise(noise);
        NvDataTool.getInstance().setProximitySensorDifference(difference);

    }

    private int getCurrentNoiseValue() {

        int value = 0;

        String string = FileReadWrite.readFileByLines(PSENSOR_PATH);
        String[] keyValues = string.split(";");
        for (String extra : keyValues) {
            extra = extra.trim();
            extra = extra.replaceAll(" ", "");
            String[] keyValue = extra.split("=");
            if (keyValue == null || keyValue.length != 2) {
                continue;
            }
            try {

                if (keyValue[0].equals("ps_noise")) {
                    value = Integer.decode(keyValue[1]);
                    break;
                }

            } catch (Exception e) {

            }
        }

        return value;
    }


    private int getCurrentDifferenceValue() {

        int value = 0;

        String string = FileReadWrite.readFileByLines(PSENSOR_PATH);
        String[] keyValues = string.split(";");
        for (String extra : keyValues) {
            extra = extra.trim();
            extra = extra.replaceAll(" ", "");
            String[] keyValue = extra.split("=");
            if (keyValue == null || keyValue.length != 2) {
                continue;
            }
            try {

                if (keyValue[0].equals("ps_difference")) {
                    value = Integer.decode(keyValue[1]);
                    break;
                }

            } catch (Exception e) {

            }
        }

        return value;
    }

    private int getCurrentSensorValue() {

        int value = 0;

        String string = FileReadWrite.readFileByLines(PSENSOR_PATH);
        String[] keyValues = string.split(";");
        for (String extra : keyValues) {
            extra = extra.trim();
            extra = extra.replaceAll(" ", "");
            String[] keyValue = extra.split("=");
            if (keyValue == null || keyValue.length != 2) {
                continue;
            }
            try {

                if (keyValue[0].equals("ps_value")) {
                    value = Integer.decode(keyValue[1]);
                    break;
                }

            } catch (Exception e) {

            }
        }

        return value;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        sensorShelterDistance = (int) event.values[0];
        Log.e(TAG, "sensorShelterDistance = " + sensorShelterDistance);

        if (pSensorState == PSensorState.tune_20cm_check_distance_doing) {
            updateInfo(false);
        }

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }


    private List<Integer> sensorValueList = new ArrayList<>();
    private final static int MAX_COUNT_SENSOR_VALUE = 200;
    private final static int AVERAGE_COUNT_SENSOR_VALUE = 30;//取平均值.
    private int sensorValueCount = 0;
    private int sensorValueNoiseAverage = 0;
    private int sensorValue20cmAverage = 0;
    private int sensorDiffCount = 0;//录取差值时,超过MAX_SENSOR_DIFF_COUNT_VALID次后, 才去实时判断sensorDiffAverage减sensorNoiseAverage值
    private final static int MAX_SENSOR_DIFF_COUNT_VALID = 80;

    private int sensorShelterDistance = 0;
    private int sensorCheckShelterCount = 0;
    private final static int SENSOR_CHECK_SHELTER_COUNT_VALID = 3;


    private Handler myHandler;

    private PSensorState pSensorState = PSensorState.tune_noise_start;

    private void updateInfo(boolean getSensorValueEnd) {
        switch (pSensorState) {
            case tune_noise_start:
                txtInfo.setTextColor(Color.BLACK);
                txtInfo.setText(R.string.proximity_sensor_tune_noise_info);
                pSensorState = PSensorState.tune_noise_doing;
                sensorValueNoiseAverage = 0;
                btnCalibration20cm.setEnabled(false);
                btnCalibrationNoise.setEnabled(false);

                startTuneOfProximitySensor();

                break;
            case tune_noise_doing: {

                sensorValueNoiseAverage = getAverageSensorValue();
                String txt = getString(R.string.proximity_sensor_step_noise_value, sensorValueNoiseAverage);

                txtInfo.setTextColor(Color.BLUE);
                txtInfo.setText(txt);

                if (getSensorValueEnd) {
                    pSensorState = PSensorState.tune_noise_end;
                    updateInfo(false);
                }

                break;
            }
            case tune_noise_end: {

                StringBuilder sb = new StringBuilder("");

                sensorValueNoiseAverage = getAverageSensorValue();
                String txt = getString(R.string.proximity_sensor_step_noise_value, sensorValueNoiseAverage);

                sb.append(txt);

                String txt20cm = getString(R.string.proximity_sensor_step_20cm_difference_value, getCurrentDifferenceValue());

                sb.append(txt20cm);

                String txtTip = getString(R.string.proximity_sensor_start_difference_tune);
                sb.append(txtTip);

                txtInfo.setTextColor(Color.BLUE);
                txtInfo.setText(sb.toString());

                btnCalibration20cm.setEnabled(true);
                btnCalibrationNoise.setEnabled(true);

                break;
            }
            case tune_20cm_start:

                txtInfo.setTextColor(Color.BLACK);
                txtInfo.setText(R.string.proximity_sensor_tune_difference_info);
                pSensorState = PSensorState.tune_20cm_doing;
                sensorValue20cmAverage = 0;
                sensorDiffCount = 0;
                btnCalibration20cm.setEnabled(false);
                btnCalibrationNoise.setEnabled(false);

//                startTuneOfProximitySensor();

                break;
            case tune_20cm_doing: {

                StringBuilder sb = new StringBuilder("");
                String txtNoise = getString(R.string.proximity_sensor_step_noise_value, sensorValueNoiseAverage);

                sb.append(txtNoise);

                sensorValue20cmAverage = getAverageSensorValue();

                int difference = sensorValue20cmAverage - sensorValueNoiseAverage;

                if (++sensorDiffCount > MAX_SENSOR_DIFF_COUNT_VALID &&
                        !(difference >= STK_MIN_20CM_DIFF && difference <= STK_MAX_20CM_DIFF)) {

                    pSensorState = PSensorState.tune_20cm_end;
                    updateInfo(false);
                    myHandler.removeMessages(MSG_GET_SENSOR_VALUE);

                } else {

                    String txt = getString(R.string.proximity_sensor_step_20cm_difference_value, difference);

                    sb.append(txt);

                    txtInfo.setTextColor(Color.BLUE);
                    txtInfo.setText(sb.toString());

                    if (getSensorValueEnd) {
                        pSensorState = PSensorState.tune_20cm_end;
                        updateInfo(false);
                    }
                }

                break;
            }
            case tune_20cm_end: {

                myHandler.removeMessages(MSG_GET_SENSOR_VALUE);

                StringBuilder sb = new StringBuilder("");

                int difference = sensorValue20cmAverage - sensorValueNoiseAverage;

                if (!(difference >= STK_MIN_20CM_DIFF && difference <= STK_MAX_20CM_DIFF)) {

                    String fail = getString(R.string.proximity_sensor_tune_prefix_error,
                            sensorValueNoiseAverage, STK_MAX_NOISE,
                            difference, STK_MIN_20CM_DIFF, STK_MAX_20CM_DIFF);
                    String more = getString(R.string.proximity_sensor_tune_postfix_retry);
                    sb.append(fail);
                    sb.append(more);

                    txtInfo.setTextColor(Color.RED);
                    txtInfo.setText(sb.toString());

                    btnCalibration20cm.setEnabled(true);
                    btnCalibrationNoise.setEnabled(true);


                } else {

                    String ok = getString(R.string.proximity_sensor_tune_ok, sensorValueNoiseAverage, difference);
                    String more = getString(R.string.proximity_sensor_step_distance_value);
                    sb.append(ok);
                    sb.append(more);
                    txtInfo.setTextColor(Color.BLUE);
                    txtInfo.setText(sb.toString());

                    writeTuneResultIntoKernel();//将校准值写入内核.

                    //禁止操作人员乱点
                    btnCalibration20cm.setEnabled(false);
                    btnCalibrationNoise.setEnabled(false);


                    pSensorState = PSensorState.tune_20cm_check_distance_start;
                    updateInfo(false);

                }

                break;
            }
            case tune_20cm_check_distance_start: {

                StringBuilder sb = new StringBuilder("");

                int difference = sensorValue20cmAverage - sensorValueNoiseAverage;

                String ok = getString(R.string.proximity_sensor_tune_ok, sensorValueNoiseAverage, difference);
                String more = getString(R.string.proximity_sensor_step_distance_value);
                sb.append(ok);
                sb.append(more);
                txtInfo.setTextColor(Color.BLUE);
                txtInfo.setText(sb.toString());

                pSensorState = PSensorState.tune_20cm_check_distance_doing;
                updateInfo(false);

                //检测超时, 则为失败. 需要重新校准
                myHandler.sendEmptyMessageDelayed(MSG_CHECK_DISTANCE, TIMEOUT_CHECK_DISTANCE);

                break;
            }
            case tune_20cm_check_distance_doing: {
                StringBuilder sb = new StringBuilder("");

                int difference = sensorValue20cmAverage - sensorValueNoiseAverage;

                String ok = getString(R.string.proximity_sensor_tune_ok, sensorValueNoiseAverage, difference);
                String more = getString(R.string.proximity_sensor_step_distance_value);
                String distance = getString(R.string.proximity_sensor_distance_tips, sensorShelterDistance);
                sb.append(ok);
                sb.append(more);
                sb.append(distance);
                txtInfo.setTextColor(Color.BLUE);
                txtInfo.setText(sb.toString());

                if (sensorShelterDistance == CHECK_DISTANCE) {
                    sensorCheckShelterCount++;
                }
                if (sensorCheckShelterCount > SENSOR_CHECK_SHELTER_COUNT_VALID) {
                    pSensorState = PSensorState.tune_20cm_check_distance_end;
                    updateInfo(false);
                }


                break;
            }
            case tune_20cm_check_distance_end:
                myHandler.removeMessages(MSG_CHECK_DISTANCE);

                saveIntoNVRom();

                startAutoPass(R.string.test_proximity_sensor_pass);

                break;
            case tune_20cm_check_distance_fail:
                String fail = getString(R.string.proximity_sensor_distance_fail);
                txtInfo.setTextColor(Color.RED);
                txtInfo.setText(fail);

                sensorValueNoiseAverage = 0;
                sensorValue20cmAverage = 0;
                writeTuneResultIntoKernel();

                btnCalibration20cm.setEnabled(false);
                btnCalibrationNoise.setEnabled(true);

                break;
            default:
                break;
        }
    }

    private synchronized void startGetSensorValue(int delay_ms) {
        sensorValueCount = 0;
        sensorValueList.clear();
        myHandler.sendEmptyMessageDelayed(MSG_GET_SENSOR_VALUE, delay_ms);

        updateInfo(false);
    }

    private synchronized void pushSensorValueList(int sensorValue) {

        if (sensorValueList.size() >= AVERAGE_COUNT_SENSOR_VALUE) {
            sensorValueList.remove(sensorValueList.size() - 1);
        }

        sensorValueList.add(0, sensorValue);

        boolean end = true;
        if (++sensorValueCount < MAX_COUNT_SENSOR_VALUE) {
            myHandler.sendEmptyMessageDelayed(MSG_GET_SENSOR_VALUE, DELAY_GET_SENSOR_VALUE);
            end = false;
        }

        updateInfo(end);


    }

    private synchronized int getAverageSensorValue() {
        long sum = 0;
        for (int val : sensorValueList) {
            sum += val;
        }
        return (int) (sum / sensorValueList.size());
    }


    private final static int MSG_GET_SENSOR_VALUE = 100;
    private final static int DELAY_GET_SENSOR_VALUE = 20;//20 ms, 1 second means 50 times

    private final static int MSG_CHECK_DISTANCE = 110;
    private final static int TIMEOUT_CHECK_DISTANCE = 15 * 1000;

    private final static class MyHandler extends Handler {

        private final WeakReference<ProximitySensorTestActivity> reference;

        public MyHandler(ProximitySensorTestActivity activity) {
            reference = new WeakReference<ProximitySensorTestActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            ProximitySensorTestActivity activity = reference.get();
            if (activity == null) {
                return;
            }
            final int what = msg.what;
            switch (what) {
                case MSG_GET_SENSOR_VALUE:

                    int val = activity.getCurrentSensorValue();
                    activity.pushSensorValueList(val);

                    break;
                case MSG_CHECK_DISTANCE:
                    activity.pSensorState = PSensorState.tune_20cm_check_distance_fail;
                    activity.updateInfo(false);
                    break;
                default:
                    break;
            }
        }
    }

    private enum PSensorState {
        tune_noise_start,
        tune_noise_doing,
        tune_noise_end,
        tune_20cm_start,
        tune_20cm_doing,
        tune_20cm_end,

        //测试20cm距离处遮挡物是否存在.
        tune_20cm_check_distance_start,
        tune_20cm_check_distance_doing,
        tune_20cm_check_distance_end,
        tune_20cm_check_distance_fail,

    }

}
